<?php
/**
 * Created by PhpStorm.
 * User: longli
 * VX: isa1589518286
 * Date: 2020/09/04
 * Time: 19:15
 * @link http://www.lmterp.cn
 */

namespace app\common\service\channel;

use app\common\library\Tools;
use app\common\model\ChannelOrders;
use app\common\model\Orders;
use app\common\service\logistics\ChannelService;
use think\facade\Log;

/**
 * 华翰
 * Class HuaHanYiSuTong
 * @package app\common\service\channel
 */
class HuaHan extends BaseChannel
{
    public static $tokenField = [
        'required' => [ // 必填字段
            [
                'type' => 'text',
                'name' => 'app_key',
                'field' => 'app_key',
            ],
            [
                'type' => 'text',
                'name' => 'app_token',
                'field' => 'app_token',
            ]
        ],
        'option' => [ // 可选字段
        ],
    ];

    /**
     * @inheritDoc
     */
    public function getTrackNumber()
    {
        $xml = $this->buildBody($this->getTrackBody(), "createOrder");
        $response = Tools::curlPost($this->channel->carrier->api_base_url, $xml);
        if($this->hasError($response, $trackInfo))
        {
            $this->resetOrderStatus();
            $this->order->order_status = Orders::ORDER_DECLARE_ERROR;
            $this->order->save();
            return false;
        }

        $track = trim($trackInfo['order_code']);
        $this->order->save([
            'track_num' => $track,
            'order_status'   => Orders::ORDER_DECLARE_SUCC,
            'track_num_time' => Tools::now(),
            'logistics_name' => $this->channel->carrier->carrier_name,
        ]);
        ChannelService::getInstance()->saveChannelOrders([
            'order_id' => $this->order->order_id,
            'channel_id' => $this->channel->channel_id,
            'country_code' => $this->order->buyer_country_code,
            'track_num' => $track,
            'weight' => $this->order->weight,
        ]);
        return $track;
    }

    /**
     * 获取追踪号请求体
     * @return array
     * @date 2020/09/04
     * @author longli
     */
    protected function getTrackBody()
    {
        //海关申报信息
        $itemArr = [];
        foreach($this->order->detail as $detail)
        {
            $weight = 0.1;
            // 计算申报重量，默认为 0.1
            if($detail->declare_weight > 0) $weight = $detail->declare_weight / 1000;
            $itemArr[] = [
                'invoice_enname' => $detail->declare_en, // 海关申报品名
                'invoice_cnname' => $detail->declare_ch, // 中文海关申报品名
                'invoice_weight' => $weight, // 申报重量，单位 KG, 精 确到三位小数。
                'invoice_quantity' => $detail->qty, // 数量
                // 'unit_code' => '', // 单 位 (MTR( 米 ), PCE(件), SET(套)), 默认 PCE
                'invoice_unitcharge' => $detail->declare_price == 0 ? 1 : $detail->declare_price, // 单价
                'invoice_currencycode' => $this->order->currency, // 申 报 币 种 ， 默 认 为 USD(美元)
                // 'hs_code' => '', // 海关协制编号
                // 'invoice_note' => '', // 配货信息
                // 'invoice_url' => '', // 销售地址
                // 'sku' => '', // 产品 SKU 编码
            ];
        }
        $postData = [
            'reference_no' => $this->order->order_sn, // 客户订单号
            'shipping_method' => $this->channel->channel_code, // 配送方式
            'country_code' => $this->order->buyer_country_code, // 收件人国家二字码
            // 'extra_service' => '', // 附加服务代码，每个以英文分号“;”隔开
            'order_weight' => ($this->order->getWeight() > 0 ? $this->order->getWeight() / 1000 : 1) . '', // 订单重量单位：KG
            // 'order_pieces' => '1', // 外包装件数,默认 1
            // 'insurance_value' => '0', // 投保金额,默认 RMB
            // 'mail_cargo_type' => '4', // 包裹申报种类
            'Consignee' => [ // 收件人信息
                'consignee_company' => $this->order->buyer_company, // 收件人公司名
                'consignee_province' => $this->order->buyer_province, // 收件人省
                'consignee_city' => $this->order->buyer_city, // 收件人城市
                'consignee_street' => $this->order->buyer_address_1, // 收件人地址 1
                'consignee_postcode' => $this->order->buyer_post_code, // 收件人邮编
                'consignee_name' => Orders::getBuyerName($this->order), // 收件人姓名
                'consignee_telephone' => $this->order->buyer_phone, // 收件人电话
                'consignee_mobile' => $this->order->buyer_mobile, // 收件人手机
                // 'consignee_email' => '', // 收件人邮箱
                // 'consignee_certificatetype' => '', // 证件类型
                // 'consignee_certificatecode' => '', // 号码
                // 'consignee_credentials_period' => '', // 有效期
                // 'buyer_id' => '', // 买家 ID
                // 'consignee_doorplate' => '', // 收件人门牌号
                // 'consignee_taxno' => '', // 收件人税号
            ],
            'Shipper' => [ // 发件人信息
                // 'shipper_company' => '', // 发件人公司名
                'shipper_countrycode' => $this->sender->country_code, // 发件人国家二字码
                'shipper_province' => $this->sender->province, // 发件人省
                'shipper_city' => $this->sender->city, // 发件人城市
                'shipper_street' => $this->sender->address, // 发件人地址
                'shipper_postcode' => $this->sender->post_code, // 发件人邮编
                // 'shipper_areacode' => '', // 区域代码
                'shipper_name' =>$this->sender->name, // 发件人姓名
                'shipper_telephone' => $this->sender->phone, // 发件人电话
                'shipper_mobile' => $this->sender->mobile, // 发件人手机
                'shipper_email' => $this->sender->email, // 发件人邮箱
                'shipper_fax' => $this->sender->fax, // 发件人传真
                // 'order_note' => '', // 订单备注
            ],
            'ItemArr' => $itemArr,
        ];
        return $postData;
    }

    /**
     * 检查响应信息是否有误
     * @param array|string $result
     * @param array $body 解析后的参数
     * @return bool
     * @date 2020/09/04
     * @author longli
     */
    protected function hasError($result, & $body = null)
    {
        if(isset($result['status']) && isset($result['data']) && is_string($result['data']))
        {
            if(!$result['status'])
            {
                Log::info(sprintf("渠道【%s】服务器无法请求，错误信息【%s】，订单号【%s】", $this->channel->channel_name, json_encode($result['info']), $this->order->order_sn));
                return true;
            }
            $result = $this->parseXML($result['data']);
        }
        if(trim($result['ask']) != 'Success')
        {
            Log::info(sprintf("渠道【%s】响应错误，错误信息【%s】，订单号【%s】", $this->channel->channel_name, $result['Error']['errMessage'], $this->order->order_sn));
            return true;
        }
        $body = $result;
        return false;
    }

    /**
     * 解析 xml
     * @param string $xml xml 字符
     * @return array
     * @date 2020/09/04
     * @author longli
     */
    protected function parseXML($xml)
    {
        $temp = explode("\n", trim($xml));
        $xml = end($temp);
        $xml = substr($xml, strpos($xml,'<response>') + strlen('<response>'));
        $xml = substr($xml, 0, strpos($xml,'</response>'));
        return json_decode($xml, true);
    }

    /**
     * 构建请求体
     * @param array|string $postData 请求参数
     * @param string $service 请求的服务
     * @return string
     * @date 2020/09/04
     * @author longli
     */
    protected function buildBody($postData = [], $service = '')
    {
        return Tools::arrayToXml([
                "SOAP-ENV:Body" => [
                    "ns1:callService" => [
                        "paramsJson" => is_array($postData) ? json_encode($postData) : $postData,
                        "appToken" => $this->channel->carrier->token->app_token,
                        "appKey" => $this->channel->carrier->token->app_key,
                        "service" => $service,
                    ]
                ]
            ],
            [
                "name" => "SOAP-ENV:Envelope",
                "attribute" => [
                    "xmlns:SOAP-ENV" => "http://schemas.xmlsoap.org/soap/envelope/",
                    "xmlns:ns1" => "http://www.example.org/Ec/"
                ]
            ]);
    }

    /**
     * 获取面单
     * @param string $type 面单类型，可选值:1(10*10)，2(A4纸),3(10*15)，默认为1
     * @return string
     * @date 2020/09/04
     * @author longli
     */
    public function getLabel()
    {
        if(empty($this->order->track_num)) return false;
        $label = [
            '100100' => 1,
            '210297' => 2,
            '100150' => 3,
        ];
        $key = "{$this->channel->label_width}{$this->channel->label_height}";
        $type = isset($label[$key])
            ? $label[$key]
            : 1;
        $requestBody = $this->buildBody([
            'reference_no' => $this->order->track_num,
            'label_type' => $type,
            'label_content_type' => 4,
        ], 'getLabelUrl');
        $response = Tools::curlPost($this->channel->carrier->api_base_url, $requestBody);
        if(!$this->hasError($response, $labelData))
        {
            $response = Tools::curlGet($labelData['url']);
            $this->order->label_url = self::saveLabel($response['data']);
            $this->order->save();

            ChannelService::getInstance()->saveChannelOrders([
                'order_id' => $this->order->order_id,
                'channel_id' =>$this->channel->channel_id,
                'track_num' => $this->order->track_num,
                'label_url' => $this->order->label_url,
            ]);
        }
        else
        {
            $this->order->order_status = Orders::ORDER_LABEL_ERROR;
            $this->order->save();
        }
        return $this->order->label_url;
    }

    /**
     * @inheritDoc
     */
    public function cancelOrder()
    {
        $requestBody = $this->buildBody([
            "reference_no" => $this->order->track_num,
        ], 'cancelOrder');
        $response = Tools::curlPost($this->channel->carrier->api_base_url, $requestBody);
        if($this->hasError($response)) return false;
        ChannelService::getInstance()->saveChannelOrders([
            'order_id' => $this->order->order_id,
            'channel_id' =>$this->channel->channel_id,
            'track_num' => $this->order->track_num,
            'is_cancel' => ChannelOrders::IS_YES,
        ]);
        return true;
    }

    public function getTrackInfo($trackNum = '')
    {
        $codes = empty($trackNum)
                ? [$this->order->track_num]
                : (!is_array($trackNum) ? explode(',', $trackNum) : $trackNum);
        $requestBody = $this->buildBody([
            "codes" => $codes,
        ], 'getCargoTrack');
        $response = Tools::curlPost($this->channel->carrier->api_base_url, $requestBody);
        if($this->hasError($response, $data)) return "";
        foreach($data['Data'] as $item)
        {
            ChannelService::getInstance()->saveChannelOrders([
                'order_id' => $this->order->order_id,
                'channel_id' =>$this->channel->channel_id,
                'track_num' => $item['Code'],
                'track_info' => json_encode($item),
                // 'country_code' => '',
            ]);
        }
        return json_encode($data['Data']);
    }

    public function computeCharge($data = [])
    {
        if(empty($data))
        {
            $weight = $this->order->weight;
            $country = $this->order->buyer_country_code;
        }
        else
        {
            $country = $data['country_code'];
            $weight = $this->order->getWeight();
        }
        $weight = $weight ? $weight / 1000 : 1;
        $requestBody = $this->buildBody([
            "country_code" => $country,
            "weight" => $weight,
        ], 'feeTrail');
        $response = Tools::curlPost($this->channel->carrier->api_base_url, $requestBody);
        if($this->hasError($response, $data)) return [];
        $ret = [];
        foreach($data['data'] as $item)
        {
            $ret[] = [
                'weight' => $item['ChargeWeight'], // 重量 KG
                'date' => $item['Effectiveness'], // 时效
                'logistics_price' => $item['TotalFee'], // 总价
                'name_en' => $item['ServiceEnName'], // 服务商英文名
                'name_ch' => $item['ServiceCnName'], // 服务商中文名
                'is_track' => strtoupper(trim($item['Traceability'])) == 'Y', // 是否可追踪
            ];
        }
        return $ret;
    }
}